/*    
    This file is part of AWC2 system.

    AWC2 is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    AWC2 is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with AWC2.  If not, see <http://www.gnu.org/licenses/>.
*/
#include "httpd.h"
#include "http_config.h"
#include "apr_buckets.h"
#include "apr_general.h"
#include "apr_lib.h"
#include "util_filter.h"
#include "http_request.h"
#include "http_log.h"
#include "http_core.h"
#include "http_protocol.h"
#include "ap_config.h"
#include "ap_provider.h"

#include <stdarg.h>
#include "http_main.h"
#include "util_script.h"

#include <ctype.h>

#include "apr.h"
#include "ap_regex.h"
#include "apr_dbd.h" 
#include <buffio.h>
#include <stdio.h>
#include <errno.h>

// Apache Web Content Cleaner (AWC2)

//sudo /opt/lampp/bin/apxs -i -a -c mod_awcc2.c -L/opt/lampp/lib

#define MAXREGEX 10
#define NREGEX 5


//Nome filtro
static const char FilterName[]="AWC2";
module AP_MODULE_DECLARE_DATA Awc2_module; 

typedef struct
{
    int bEnabled;
} AwccConfig;

typedef struct {
    request_rec *r;
    apr_file_t *fp;
    int nregex;
    const apr_dbd_driver_t *driver;
    apr_dbd_t *handle;
    apr_dbd_prepared_t *statementBL;
    apr_dbd_prepared_t *statementWL;
    apr_dbd_prepared_t *statementRL;
    ap_regex_t regtype[NREGEX];
} Context;

static void *AwccCreateServerConfig(apr_pool_t *p,server_rec *s) {
    AwccConfig *pConfig=apr_pcalloc(p,sizeof *pConfig);

    pConfig->bEnabled=0;

    return pConfig;
}

static void AwccInsertFilter(request_rec *r) {
    AwccConfig *pConfig=ap_get_module_config(r->server->module_config, &Awc2_module);

    if(!pConfig->bEnabled)
	return;
    
    // Aggiungo il filtro di uscita se abilitato
    ap_add_output_filter(FilterName,NULL,r,r->connection);
}

// Compilazione della regex
static int regexCompile (ap_regex_t * r, char * regex_text) {
    int status = ap_regcomp (r, regex_text, AP_REG_EXTENDED|AP_REG_NEWLINE|AP_REG_ICASE);
    if (status != 0) {
        //ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server,"Errore durante la compilazione della regex");
        
        return 1;
    }
    return 0;
}

static char *extractDomain(char *buf, Context *ctx) {
    ap_regex_t regtype;
    ap_regex_t regtype1;
    char * regtemp;
    char * p = buf;
    char * start_text = buf;
    int start, finish;
    char * result;
    
    apr_file_printf(ctx->fp, "Estrazione dominio.%s\n", buf);
    
    // IP ADDRESS
    regtemp = "[http|ftp|https]://([0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3})(:[0-9]+)?/?.*";
    regexCompile(&regtype1, regtemp);
    
    const int n_matches = 10;
    ap_regmatch_t m[n_matches];

    int nomatch = ap_regexec (&regtype1, p, n_matches, m, 0);
    if(!nomatch) {
    
        start = m[1].rm_so ;
        finish = m[1].rm_eo;
        
        result = apr_pcalloc(ctx->r->pool, (finish-start) * sizeof(char *));
        
        result = (char *)apr_psprintf(ctx->r->pool, "%.*s", (finish - start), start_text + start);
        
        apr_file_printf(ctx->fp,"Dominio estratto: %s\n", result);
        
        return result;
    }
    
    // DOMAIN with 2 first level: co.uk
    regtemp = "[^:]://([^/]+\\.)*([^/\\.]+(\\.[^/\\.]{2,3}){2})/?.*";
    regexCompile(&regtype, regtemp);
    
    
    //ap_regmatch_t m[n_matches];

    nomatch = ap_regexec (&regtype, p, n_matches, m, 0);
    if(!nomatch) {
    
        start = m[2].rm_so ;
        finish = m[2].rm_eo;
        
        result = apr_pcalloc(ctx->r->pool, (finish-start) * sizeof(char *));
        
        result = (char *)apr_psprintf(ctx->r->pool, "%.*s", (finish - start), start_text + start);
        
        apr_file_printf(ctx->fp,"Dominio estratto: %s\n", result);
        
        return result;
    }
    
    regtemp = "[^:]://([^/]+\\.)*([^/\\.]+(\\.[^/\\.]{1,4}){1})/?.*";
    regexCompile(&regtype, regtemp);
    
    //ap_regmatch_t m[n_matches];

    nomatch = ap_regexec (&regtype, p, n_matches, m, 0);
    if(nomatch) {
        // errore non è un url
        ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server, "extractDomain: dominio non trovato!");
        apr_file_printf(ctx->fp," Dominio non trovato!!!\n");
        return NULL;
    }
    
    start = m[2].rm_so ;
    finish = m[2].rm_eo;
    
    result = apr_pcalloc(ctx->r->pool, (finish-start) * sizeof(char *));
    
    result = (char *)apr_psprintf(ctx->r->pool, "%.*s", (finish - start), start_text + start);
    
    apr_file_printf(ctx->fp,"Dominio estratto: %s\n", result);
    
    return result;
}


static int inBlacklist(char *domain, Context *ctx) {
    apr_dbd_results_t *res = NULL;
    int rv;
    //char * domain;
    
    //domain = extractDomain(buf, ctx);
    
    rv = apr_dbd_pvselect(ctx->driver, ctx->r->pool, ctx->handle, &res, ctx->statementBL, 0, domain, NULL); 
    if (rv) { 
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server, "Errore apr_dbd_pvselect: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0; 
    }
   
    rv = apr_dbd_num_tuples(ctx->driver, res);
    if( rv == -1 ) {
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_num_tuples: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0;
    }
    
    apr_file_printf(ctx->fp,"Domini '%s' trovati in blacklist: %d\n", domain, rv);
    
    return rv;
}

static int inWhitelist(char *domain, Context *ctx) {
    apr_dbd_results_t *res = NULL;
    int rv;
    //char * domain;
    apr_file_printf(ctx->fp,"Entro in inWhitelist(%s)\n", domain);
    //domain = extractDomain(buf, ctx);
    
    rv = apr_dbd_pvselect(ctx->driver, ctx->r->pool, ctx->handle, &res, ctx->statementWL, 0, domain, NULL); 
    if (rv) { 
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server, "Errore apr_dbd_pvselect: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0; 
    }
   
    rv = apr_dbd_num_tuples(ctx->driver, res);
    if( rv == -1 ) {
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_num_tuples: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0;
    }
    
    apr_file_printf(ctx->fp,"Domini '%s' trovati in whitelist: %d\n", domain, rv);
    
    return rv;
}

static float inReliable(char *domain, Context *ctx) {
    apr_dbd_results_t *res = NULL;
    int rv;
    apr_dbd_row_t *row = NULL; 
    const char *entry = NULL;
    //char * domain;
    apr_file_printf(ctx->fp,"Entro in inReliable()\n");
    //domain = extractDomain(buf, ctx);
    
    rv = apr_dbd_pvselect(ctx->driver, ctx->r->pool, ctx->handle, &res, ctx->statementRL, 0, domain, NULL); 
    if (rv) { 
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server, "Errore apr_dbd_pvselect: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0; 
    }
   
    rv = apr_dbd_num_tuples(ctx->driver, res);
    if( rv == -1 ) {
       ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_num_tuples: %s", apr_dbd_error(ctx->driver, ctx->handle, rv));
       return 0;
    }
    
    apr_file_printf(ctx->fp,"Domini '%s' trovati in reliablelist: %d\n", domain, rv);
    if(rv) {
        apr_dbd_get_row(ctx->driver, ctx->r->pool, res, &row, -1);
        entry = apr_dbd_get_entry(ctx->driver, row, 2);
        
        apr_file_printf(ctx->fp,"Il voto di questo dominio è: %s\n", entry);
        
        return atof(entry);
    }
    
    return rv;
}

static char * apr_realloc(apr_pool_t *pool, char *ptr, size_t size) {
    char *new_area;
    int len;

    new_area = apr_palloc(pool, size);
    len = strlen(ptr);
    memcpy (new_area, ptr, len);


    return new_area;
}

static int clean(ap_regex_t *regtype, char **dataout, int *lenout,  Context *ctx) {
    conn_rec *c = ctx->r->connection;
    char * p = *dataout;
    char * start_text = *dataout;
    char * datatemp = apr_palloc(ctx->r->pool, *lenout);
    strncpy(datatemp, *dataout, *lenout);
    
    char * write = datatemp;
    int lenin = *lenout;
    
    int removed = 0;
    
    /* "N_matches" is the maximum number of matches allowed. */
    const int n_matches = 10;
    /* "M" contains the matches found. */
    ap_regmatch_t m[n_matches];

    while (1) {
        int i = 0, from, to;
        int nomatch = ap_regexec (regtype, p, n_matches, m, 0);
        if (nomatch) {
            apr_file_printf(ctx->fp,"NO MATCH\n");
            if(removed) {
                char *read;
                read  = p;

                while(read < start_text+lenin) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
                apr_file_printf(ctx->fp,"\n");
                
                datatemp[*lenout-1] = '\0';
                *dataout = datatemp;
                //apr_file_printf(ctx->fp,"Stringa di uscita (%d): %s\n", *lenout, datatemp);
            }
            
            return nomatch;
        }
        
        from = m[0].rm_so + (p - start_text);
        to = m[0].rm_eo + (p - start_text);
        
        for (i = 1; i < n_matches; i++) {
            int start, finish, result;
            int h;
            float rate;
            char *buf;
            char *read, *temp1, *end, *domain;
            
            if (m[i].rm_so == -1) {
                break;
            }
            start = m[i].rm_so + (p - start_text);
            finish = m[i].rm_eo + (p - start_text); 
            
            apr_file_printf(ctx->fp,"Match trovato: %.*s. \n", (finish - start), start_text + start);
            buf = apr_pcalloc(ctx->r->pool, (finish-start) * sizeof(char *));
            
            buf = (char *)apr_psprintf(ctx->r->pool, "%.*s", (finish - start), start_text + start);
    
            domain = extractDomain(buf, ctx);
            
            // Controllo se il dominio è ok, altrimenti lo elimino.
            //result = inBlacklist(domain, ctx);
            // If the domain is in the blacklist, it deletes him.
            if(inBlacklist(domain, ctx)) {
                apr_file_printf(ctx->fp," Eliminato\n");
                //apr_file_printf(ctx->fp,"stringa intera: %.*s.\n", (to - from), start_text + from);

                
                read  = p;
                while(read < p+m[0].rm_so) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
                
                removed = 1;
                //apr_file_printf(ctx->fp,"Cancellati %d caratteri\n", to-from);
                
                *lenout -= (to-from);
            }
            else if(inWhitelist(domain, ctx)){
                read  = p;
                while(read < p+m[0].rm_eo) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
            }
            else if(rate = inReliable(domain, ctx)) {
                char *opencomment;
                //apr_palloc(ctx->r->pool, size);
                
                opencomment = (char *)apr_psprintf(ctx->r->pool, "<!-- Reliability: %.1f -->", rate);
                //opencomment = apr_pstrcat(ctx->r->pool, rate, "-->", NULL);
                
                char *closecomment = "<!-- end -->";
                int len_opencomment = strlen(opencomment),
                    len_closecomment = strlen(closecomment);
                char *ps;
                
                // Copio tutto quello che viene prima
                read = p;
                while(read < p+m[0].rm_so) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
                
                // Non basta il vecchio spazio
                if(lenin - *lenout < len_opencomment + len_closecomment) {
                    int diff = write - datatemp;
                    datatemp = apr_realloc( ctx->r->pool, datatemp, lenin - (lenin - *lenout) + len_opencomment + len_closecomment);
                    write = datatemp + diff;
                    apr_file_printf(ctx->fp," Ho riallocato il buffer.\n");
                }
                
                ps = opencomment;
                while(ps < opencomment+len_opencomment) {
                    *write = *ps;
                    write++;
                    ps++;
                }
                
                // Inserisco il tag
                while(read < p+m[0].rm_eo) {
                    *write = *read;
                    write++;
                    read++;
                }
                
                // Inserisco il commento finale
                ps = closecomment;
                while(ps < closecomment+len_closecomment) {
                    *write = *ps;
                    write++;
                    ps++;
                }
                removed = 1;
                
                *lenout += len_opencomment + len_closecomment;
            }
            else if(domain == NULL) {
                read  = p;
                while(read < p+m[0].rm_eo) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
            } else {
                char *opencomment;
                //apr_palloc(ctx->r->pool, size);
                
                opencomment = (char *)apr_psprintf(ctx->r->pool, "<!-- Reliability: 0.0 -->");
                //opencomment = apr_pstrcat(ctx->r->pool, rate, "-->", NULL);
                
                char *closecomment = "<!-- end -->";
                int len_opencomment = strlen(opencomment),
                    len_closecomment = strlen(closecomment);
                char *ps;
                
                // Copio tutto quello che viene prima
                read = p;
                while(read < p+m[0].rm_so) {
                    *write = *read;
                    //apr_file_printf(ctx->fp,"%c", *write);
                    write++;
                    read++;
                }
                
                // Non basta il vecchio spazio
                if(lenin - *lenout < len_opencomment + len_closecomment) {
                    int diff = write - datatemp;
                    datatemp = apr_realloc( ctx->r->pool, datatemp, lenin - (lenin - *lenout) + len_opencomment + len_closecomment);
                    write = datatemp + diff;
                    apr_file_printf(ctx->fp," Ho riallocato il buffer.\n");
                }
                
                ps = opencomment;
                while(ps < opencomment+len_opencomment) {
                    *write = *ps;
                    write++;
                    ps++;
                }
                
                // Inserisco il tag
                while(read < p+m[0].rm_eo) {
                    *write = *read;
                    write++;
                    read++;
                }
                
                // Inserisco il commento finale
                ps = closecomment;
                while(ps < closecomment+len_closecomment) {
                    *write = *ps;
                    write++;
                    ps++;
                }
                removed = 1;
                
                *lenout += len_opencomment + len_closecomment;
                
                
                
                
            }
            
            
            
            
        }
        
        //apr_file_printf(ctx->fp,"stampo 3 caratteri dopo m[0].rm_eo: %.*s.\n", 5, p+m[0].rm_eo);
        
        p += m[0].rm_eo;
    }
    
    return 0;
}

static char* addFilter( const char * regex, Context *ctx ) {
    char * temp;
    temp = apr_palloc(ctx->r->pool, strlen(regex) * sizeof(char *));
    strcpy(temp, regex);
    ctx->nregex++;
    
    return temp;
}

// Funzione principale che si occupa del filtraggio dei link
static void htmlCheck(char **dataout, int *lenout, const char *datain, int len, Context *ctx) {
    char ** regex_list;
    char * regtemp, * regtemp2;
    int i;
    ap_regex_t regtype;
    int result;
    conn_rec *c = ctx->r->connection;
    
    // Alloco in buffer di uscita
    *dataout = apr_bucket_alloc(len, c->bucket_alloc);
    apr_file_printf(ctx->fp,"prima della copia...\n");
    // Return Html data and size
    strncpy(*dataout, datain, len);
    *lenout = len;
    apr_file_printf(ctx->fp,"copiata...\n");
    // for each regex
    for(i=0; i<ctx->nregex; i++) {
        // compile the regex
        //apr_file_printf(ctx->fp,"compilo regexl...\n");
        //regexCompile(&regtype, regex_list[i]);
        //apr_file_printf(ctx->fp,"regex compilata...\n");
        // it does a check and it performs automatic clean. Return 0 if it was clean, else return 1 
        result = clean(&ctx->regtype[i], dataout, lenout, ctx);
        //apr_file_printf(ctx->fp,"Regex %d eseguita.\n", i);
        
    }
    
    apr_file_printf(ctx->fp,"Esco da htmlCheck.\n");
}

static void createBLStatement(Context *ctx) {
    int res;
    const char *query = "SELECT * FROM blacklist where domain = %s";
    const char *label = "verifyDomainBL"; 
    
    res = apr_dbd_prepare(ctx->driver, ctx->r->pool, ctx->handle, query, label, &ctx->statementBL);
    if(res) {
        ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_prepare: %s", apr_dbd_error(ctx->driver, ctx->handle, res));
        return;
    }
}

static void createWLStatement(Context *ctx) {
    int res;
    const char *query = "SELECT * FROM whitelist where domain = %s";
    const char *label = "verifyDomainWL"; 
    
    res = apr_dbd_prepare(ctx->driver, ctx->r->pool, ctx->handle, query, label, &ctx->statementWL);
    if(res) {
        ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_prepare: %s", apr_dbd_error(ctx->driver, ctx->handle, res));
        return;
    }
}

static void createRLStatement(Context *ctx) {
    int res;
    const char *query = "SELECT * FROM reliable where domain = %s";
    const char *label = "verifyDomainRL"; 
    
    res = apr_dbd_prepare(ctx->driver, ctx->r->pool, ctx->handle, query, label, &ctx->statementRL);
    if(res) {
        ap_log_error(APLOG_MARK, APLOG_ERR, NULL, ctx->r->server,"Errore apr_dbd_prepare: %s", apr_dbd_error(ctx->driver, ctx->handle, res));
        return;
    }
}

static apr_status_t AwccOutFilter(ap_filter_t *f, apr_bucket_brigade *pbbIn) {
    request_rec *r = f->r;
    conn_rec *c = r->connection;
    apr_bucket *pbktIn;
    apr_bucket_brigade *pbbOut;
    char *filename, *dbfilename;
    
    const char *data;
    char *buffer;
    apr_size_t len;
    apr_size_t n;
    apr_bucket *pbktOut;
    int htmlDataSize;
    char *htmlData = NULL;
    const char *dbname = "sqlite3";

	int rc = -1,
        rv;

    pbbOut = apr_brigade_create(r->pool, c->bucket_alloc);
    
    filename   = ap_server_root_relative(r->pool, "logs/awcc.log");
    dbfilename = ap_server_root_relative(r->pool, "tmp/db/sqlite3.db");
    
    
    Context *ctx = apr_palloc(r->pool, sizeof(Context));
    apr_file_t *fp;
    apr_status_t result;
	
    // Apro il file per il debug
    result = apr_file_open(&fp, filename,APR_APPEND | APR_WRITE | APR_CREATE, APR_OS_DEFAULT, r->pool);
    if (result != APR_SUCCESS) {
		ap_log_error(APLOG_MARK, APLOG_ERR, result, r->server,"Impossibile aprire logs/awcc.log");
	}
    
    // Inizializzo il framework per il db
    apr_dbd_init(r->pool); 
    rv = apr_dbd_get_driver(r->pool, dbname, &ctx->driver);
    if(rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,"Errore durante l'inizializzazione del framework apr_dbd per sqlite3.");
    }
    
    // Apro il file del db
    rv = apr_dbd_open(ctx->driver, r->pool, dbfilename, &ctx->handle);
    if(rv != APR_SUCCESS) {
        ap_log_error(APLOG_MARK, APLOG_ERR, rv, r->server,"Errore durante l'apertura di %s.",dbfilename);
    }
    
    // Context Initialization
    ctx->fp = fp;
    ctx->r  = r;
    ctx->statementBL = NULL;
    ctx->statementWL = NULL;
    ctx->statementRL = NULL;
    ctx->nregex      = 0;
    
    createBLStatement(ctx);
    createWLStatement(ctx);
    createRLStatement(ctx);
    
    char ** regex_list;
    // regex_list conterrà tutte le regex per i vari tag
    regex_list = apr_palloc(ctx->r->pool, MAXREGEX * sizeof(char *));
    
    regex_list[ctx->nregex] = addFilter("<script[^>]*src[^>=]*=[^>\"']*[\"']([^>\"']*)[\"'][^>]*>[^>]*/script[^>]*>", ctx);
    regex_list[ctx->nregex] = addFilter("<iframe[^>]*src[^>=]*=[^>\"']*[\"']([^>\"']*)[\"'][^>]*>[^>]*/iframe[^>]*>", ctx);
    regex_list[ctx->nregex] = addFilter("<applet[^>]*codebase[^>=]*=[^>\"']*[\"']([^>\"']+)[\"'][^>]*>", ctx);
    regex_list[ctx->nregex] = addFilter("<object[^>]*data[^>=]*=[^>\"']*[\"']([^>\"']*)[\"'][^>]*>.*/object[^>]*>", ctx);
    regex_list[ctx->nregex] = addFilter("<img[^>]*src[^>=]*=[^>\"']*[\"']([^>\"']*)[\"'][^>]*>", ctx);
    
    for(int rn=0; rn < NREGEX; rn++) {
        regexCompile(&ctx->regtype[rn], regex_list[rn]);
    }
    
    apr_file_printf(fp,"Inizio\n");
    
    while ((pbktIn = APR_BRIGADE_FIRST(pbbIn)) != APR_BRIGADE_SENTINEL(pbbIn)) {
        htmlData = NULL;
        
        
        if(!APR_BUCKET_IS_METADATA(pbktIn)) {
            apr_file_printf(fp,"Non sono metadati.\n");
            len=0;
            // read data
            rc = apr_bucket_read(pbktIn, &data, &len, APR_BLOCK_READ);
            if(rc) ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,"Problemi con la lettura del bucket");
            
            // Elimino il bucket pbktIn da PbbIn
            APR_BUCKET_REMOVE(pbktIn);
            
            // Creo una copia dei dati e li inserisco in un buffer
            buffer = apr_bucket_alloc(len, c->bucket_alloc);
            strncpy(buffer, data, len);
            buffer[len-1] = '\0';
            
            
            apr_file_printf(fp, "--- Inizio lettura ---\n%s\n--- Fine lettura (len %d)---\n", buffer, len);
            apr_file_printf(fp,"Inizio il check.\n");
            
            
            htmlCheck(&htmlData, &htmlDataSize, buffer, len, ctx);
            //apr_file_printf(fp,"I dati sono: \n%s\n--- len %d\n", htmlData, htmlDataSize);
            
            pbktOut = apr_bucket_heap_create(htmlData, htmlDataSize, NULL, c->bucket_alloc);
            //pbktOut = apr_bucket_heap_create(buffer, len, NULL, c->bucket_alloc);
            APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
            //apr_bucket_free(htmlData);
            apr_bucket_free(buffer);
            htmlDataSize = 0;
        }
        else if ( APR_BUCKET_IS_EOS(pbktIn) ) {
            apr_file_printf(fp,"EOS.\n");
            
            // Elimino il bucket pbktIn da PbbIn
            APR_BUCKET_REMOVE(pbktIn);
            APR_BRIGADE_INSERT_TAIL(pbbOut, pbktIn);
            
            continue;
        }
        else {
            apr_file_printf(fp,"Metadati.\n");
            //rc = apr_bucket_read(pbktIn, &data, &len, APR_BLOCK_READ);
            //if(rc) ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,"Problemi con la lettura del bucket");
            //pbktOut = apr_bucket_heap_create(data, len, NULL, c->bucket_alloc);
            //pbktOut = apr_bucket_heap_create(buffer, len, NULL, c->bucket_alloc);
            //APR_BRIGADE_INSERT_TAIL(pbbOut,pbktOut);
            
            APR_BRIGADE_INSERT_HEAD(pbbOut, pbktIn);
            APR_BUCKET_REMOVE(pbktIn);
        }
        
        apr_file_printf(fp,"Dati finiti si può concludere.\n");
    }
    //ap_remove_output_filter(f);
    ap_pass_brigade(f->next,pbbOut);

    apr_brigade_cleanup(pbbOut);
    apr_file_printf(fp,"Fine\n");
    
    apr_file_close(fp);
    
    apr_brigade_cleanup(pbbIn);
    rv = apr_dbd_close(ctx->driver, ctx->handle);
    ap_log_error(APLOG_MARK, APLOG_ERR, rc, r->server,"Chiusura filtro\n");
    return APR_SUCCESS;
}

static const char *AwccFilterEnable(cmd_parms *cmd, void *dummy, int arg) {
    AwccConfig *pConfig = ap_get_module_config(cmd->server->module_config, &Awc2_module);
    pConfig->bEnabled=arg;

    return NULL;
}

static const command_rec AwccFilterCmds[] = {
    AP_INIT_FLAG("Awc2Filter", AwccFilterEnable, NULL, RSRC_CONF, "Sto eseguendo il modulo Awcc su questo host"),
    { NULL }
};

static void AwccFilterRegisterHooks(apr_pool_t *p) {
    ap_hook_insert_filter(AwccInsertFilter,NULL,NULL,APR_HOOK_MIDDLE);
    
    // ap_register_output_filter() is invoked with three arguments: the filter name, the filter function pointer and the filter type
    ap_register_output_filter(FilterName,AwccOutFilter,NULL, AP_FTYPE_RESOURCE);
}

module AP_MODULE_DECLARE_DATA Awc2_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    AwccCreateServerConfig,
    NULL,
    AwccFilterCmds,
    AwccFilterRegisterHooks
};
 
 
